package org.ala.tiktools.sharddb.mybatis.pager.with_order;

import org.ala.tiktools.mybatis.executor.SqlSessionTemplateExecutor;
import org.ala.tiktools.mybatis.interfaces.AbstractOrderBy;
import org.ala.tiktools.mybatis.pager.Pager;
import org.ala.tiktools.mybatis.pager.PagerCountDO;
import org.ala.tiktools.sharddb.mybatis.executor.ShardExecutor;
import org.ala.tiktools.sharddb.mybatis.executor.ShardNoTxExecutor;
import org.ala.tiktools.sharddb.mybatis.executor.ShardSqlSessionTemplateExecutor;
import org.ala.tiktools.sharddb.mybatis.interceptor.ShardInfoThreadLocal;
import org.ala.tiktools.sharddb.mybatis.pager.parameter.ShardPagerParameter;
import org.ala.tiktools.sharddb.mybatis.router.ShardInfo;
import org.ala.tiktools.tools.CachingKVTools;
import org.ala.tiktools.tools.kv.KV;
import org.ala.tiktools.tools.kv.Maps;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * ShardPagerParameter 执行器
 *
 * @author ala
 * @date 2025-03-05 13:37
 */
public class ShardPagerParameterExecutor {
    /**
     *  查询数量极值的方法名
     */
    static final String MethodCountByQueryWithOrderBy = "countByQueryWithOrderBy";
    /**
     *  查询极值左边的数量和等于极值数量的方法名
     */
    static final String MethodCountByQueryWithExtreme = "countByQueryWithExtreme";


    /**
     *  查询总数
     */
    public ShardCountDO doPagerTotal(ShardPagerParameter parameter) {
        Map<ShardInfo, Integer> res = ShardSqlSessionTemplateExecutor.instance().selectOne(
                parameter.getAnno(), parameter.getThreadPool(),
                parameter.getCountMethod().getMethod(), parameter.getCountMethod().getArgs());
        return ShardCountDO.build(res);
    }

    /**
     *  查询单一分库分表数据
     */
    protected List doPagerOnlyOnePage(ShardPagerParameter parameter) {
        Map.Entry<ShardInfo, Object[]> e = parameter.getDataMethod().getArgs().entrySet().iterator().next();
        ShardInfo shardInfo = e.getKey();
        Object[] args = e.getValue();

//        ShardInfoThreadLocal.set(shardInfo);
        try {
            return SqlSessionTemplateExecutor.instance().selectList(
                    parameter.getDataMethod().getMethod(),
                    args,
                    Maps.build(KV.build("shardSuffix", shardInfo.getShardTable())));
        } finally {
//            ShardInfoThreadLocal.clear();
        }
    }

    /**
     *  执行list，并汇总结果
     */
    public List doPagerList(ShardPagerParameter parameter, int left, int right) {
        Pager pager = parameter.getPager();
        int no = pager.getNo(), size = pager.getSize();
        pager.no(1).size(right);
        pager.queryTotal(false);
        Map<ShardInfo, ShardExecutor.Result> r = ShardNoTxExecutor.doInvoke(parameter.getAnno(), parameter.getThreadPool(), parameter.getDataMethod().getArgs(), parameter.getDataMethod().getProxy(), parameter.getDataMethod().getMethod());
        pager.no(no).size(size);

        //  按照表名字母序来组织第一波顺序
        List<ShardInfo> shardInfos = parameter.getDataMethod().getArgs().keySet().stream().sorted((s1, s2) -> {
            if (s1.getShardDB().equals(s2.getShardDB())) {
                return s1.getShardTable().compareTo(s2.getShardTable());
            }
            return s1.getShardDB().compareTo(s2.getShardDB());
        }).collect(Collectors.toList());

        List list = new ArrayList();
        shardInfos.forEach(shardInfo -> {
            Pager l = (Pager) r.get(shardInfo).getRes();
            if (l.getDatas() != null) {list.addAll(l.getDatas());}
        });
        if (list.size() < left) {return new ArrayList();}
        list.sort(parameter.getOrderBy());
        return list.subList(left-1, Math.min(right, list.size()));
    }


    /**
     *  查询[left, mid], (mid, right]部分的数据。其中：
     *  <p> [left, mid]反序取等于extreme的部分
     *  <p> (mid, right]取顺序大于extreme的部分
     */
    public List doPagerLeftRight(ShardPagerParameter parameter, int left, int mid, int right, Object extreme) {
        Pager pager = parameter.getPager();
        int no = pager.getNo(), size = pager.getSize();
        AbstractOrderBy orderBy = parameter.getOrderBy();

        List res = new ArrayList();

        //  查询左半边数据
        pager.no(1).size(mid - left + 1);
        orderBy.negateAllItems();
        orderBy.appendStationaryNoMerge(orderBy.getCurrentItem().copy().eq(true), extreme);
        Map<ShardInfo, List<Object>> lists = ShardSqlSessionTemplateExecutor.instance().selectList(parameter.getAnno(), parameter.getThreadPool(), parameter.getDataMethod().getMethod(), parameter.getDataMethod().getArgs());
        orderBy.negateAllItems();
        orderBy.removeLastStationary();
        List leftList = new ArrayList();
        lists.values().forEach(l -> leftList.addAll(l));
        if (!leftList.isEmpty()) {
            leftList.sort((e1, e2) -> -orderBy.compare(e1, e2));
            res.addAll(leftList.subList(0, Math.min(leftList.size(), mid - left + 1)));
        }

        //  查右半边数据
        pager.no(1).size(right - mid);
        orderBy.appendStationaryNoMerge(orderBy.getCurrentItem().copy(), extreme);
        lists = ShardSqlSessionTemplateExecutor.instance().selectList(parameter.getAnno(), parameter.getThreadPool(), parameter.getDataMethod().getMethod(), parameter.getDataMethod().getArgs());
        orderBy.removeLastStationary();
        List rightList = new ArrayList();
        lists.values().forEach(l -> rightList.addAll(l));
        if (!rightList.isEmpty()) {
            rightList.sort((e1, e2) -> orderBy.compare(e1, e2));
            res.addAll(rightList.subList(0, Math.min(rightList.size(), right - mid)));
        }

        pager.no(no).size(size);
        return res;
    }


    /**
     *  查找每个分表在限制条件下的数据量，极值
     */
    public ShardPageCountDO doLimitCount(ShardPagerParameter parameter, int limit) {
        Method m = countByQueryWithOrderByMapper(parameter.getDataMethod().getMethod());

        Pager pager = parameter.getPager();
        int oldOffset = pager.getOffset(), oldLimit = pager.getLimit();
        pager.offset(0).limit(limit).queryTotal(false);
        Map<ShardInfo, PagerCountDO> res = ShardSqlSessionTemplateExecutor.instance().selectOne(parameter.getAnno(), parameter.getThreadPool(), m, parameter.getDataMethod().getArgs());
        pager.offset(oldOffset).limit(oldLimit);

        return ShardPageCountDO.build(parameter.getOrderBy(), res);
    }

    /**
     *  查询每个分表极值左边的数量和等于极值数量
     */
    public ShardPageExtremeDO doExtremeCount(ShardPagerParameter parameter, Object extreme) {
        AbstractOrderBy orderBy = parameter.getOrderBy();

        Method m = countByQueryWithExtremeMapper(parameter.getDataMethod().getMethod());
        orderBy.getCurrentItem().val(orderBy.getVal(extreme, orderBy.getCurrentItem()));
        Map<ShardInfo, List<Integer>> res = ShardSqlSessionTemplateExecutor.instance().selectList(parameter.getAnno(), parameter.getThreadPool(), m, parameter.getDataMethod().getArgs());
        orderBy.getCurrentItem().val(null);

        return ShardPageExtremeDO.build(res.entrySet().stream().collect(Collectors.toMap(
                e -> e.getKey(),
                e -> {
                    //  union的结果，当上下union返回数一样时，只会返回1条数据
                    int left = e.getValue().get(0);
                    int eq = e.getValue().size() == 1 ? left : e.getValue().get(1);
                    return ShardPageExtremeDO.PageExtremeDO.build(e.getKey(), left, eq);
                },
                (o,n) -> n
        )));
    }


    /**
     *  查询等于extreme的数据中，[left, right]范围内的数据。按字母序取数
     */
    public List doPagerNoOrder(ShardPagerParameter parameter, ShardPageCountDO pc, ShardPageExtremeDO pe) {
        Pager pager = parameter.getPager();
        pager.queryTotal(false);
        AbstractOrderBy orderBy = parameter.getOrderBy();
        //  固化eq查询条件，确定本次查询的左右界
        orderBy.appendStationaryNoMerge(orderBy.getCurrentItem().copy().eq(true), pc.getExtreme());
        int left = parameter.getLeft() - pe.getCountLeft(), right = parameter.getRight() - pe.getCountLeft();

        //  每个分表等于 extreme 的数量在 ShardPageExtremeDO 中
        Map<ShardInfo, Pager> pagers = new LinkedHashMap<>();
        int i = 1;
        //  分库分表按字母序排序，确定每个分库分表负责的区域数
        List<Map.Entry<ShardInfo, ShardPageExtremeDO.PageExtremeDO>> rs = pe.getShards().entrySet().stream().sorted((e1, e2) -> {
            ShardInfo s1 = e1.getKey(), s2 = e2.getKey();
            if (s1.getShardDB().equals(s2.getShardDB())) {return s1.getShardTable().compareTo(s2.getShardTable());}
            return s1.getShardDB().compareTo(s2.getShardDB());
        }).collect(Collectors.toList());
        for (Map.Entry<ShardInfo, ShardPageExtremeDO.PageExtremeDO> e : rs) {
            ShardInfo shardInfo = e.getKey();
            ShardPageExtremeDO.PageExtremeDO d = e.getValue();
            int[] it = intersection(i, i+d.getCountEq()-1, left, right);
            if (it != null) {
                pagers.put(shardInfo, Pager.build(1, 1).offset(it[0]).limit(it[1]));
            }
            i += d.getCountEq();
        }

        //  查询每个分表数据
        if (pagers.isEmpty()) {return new ArrayList();}
        Map<ShardInfo, Object[]> args = new HashMap<>();
        pagers.forEach(((shardInfo, p) -> {
            Object[] as = parameter.getDataMethod().getArgs().get(shardInfo);
            as[parameter.getPagerIdx()] = p;
            args.put(shardInfo, as);
        }));
        Map<ShardInfo, List<Object>> result = ShardSqlSessionTemplateExecutor.instance().selectList(parameter.getAnno(), parameter.getThreadPool(), parameter.getDataMethod().getMethod(), args);

        //  汇总结果
        List datas = new ArrayList();
        pagers.forEach(((shardInfo, pager1) -> {
            datas.addAll(result.get(shardInfo));
        }));
        return datas;
    }
    /**
     *  判断两个区域是否存在交集，若存在交集，返回相对于第一个区域的偏移量和大小
     */
    protected int[] intersection(int l1, int r1, int l2, int r2) {
        if ((l2 >= l1 && l2 <= r1) || (l1 >= l2 && l1 <= r2)) {
            int l = Math.max(l1, l2), r = Math.min(r1, r2);
            return new int[] {Math.max(l - l1, 0), r - l + 1};
        }
        return null;
    }


    protected Map<Method, Method> countByQueryWithOrderByMapper = new HashMap<>();
    /**
     *  方法名与countByQueryWithOrderBy映射
     */
    protected Method countByQueryWithOrderByMapper(Method dataMethod) {
        Method m = countByQueryWithOrderByMapper.get(dataMethod);
        if (m == null) {
            synchronized (dataMethod) {
                m = countByQueryWithOrderByMapper.get(dataMethod);
                if (m == null) {
                    m = ClassUtils.getMethod(dataMethod.getDeclaringClass(), MethodCountByQueryWithOrderBy, (Class<?>[]) null);
                    countByQueryWithOrderByMapper.put(dataMethod, m);
                }
            }
        }
        return m;
    }


    protected Map<Method, Method> countByQueryWithExtremeMapper = new HashMap<>();
    /**
     *  方法名与countByQueryWithExtreme映射
     */
    protected Method countByQueryWithExtremeMapper(Method dataMethod) {
        Method m = countByQueryWithExtremeMapper.get(dataMethod);
        if (m == null) {
            synchronized (dataMethod) {
                m = countByQueryWithExtremeMapper.get(dataMethod);
                if (m == null) {
                    m = ClassUtils.getMethod(dataMethod.getDeclaringClass(), MethodCountByQueryWithExtreme, (Class<?>[]) null);
                    countByQueryWithExtremeMapper.put(dataMethod, m);
                }
            }
        }
        return m;
    }

}
